Vue + Koa2 移动电商项目学习笔记

Vue + Koa2 移动电商项目学习笔记 ( 一 )

开发环境搭建

  1. 建立文件夹

    新建 SmileVue 文件夹

  2. 使用 vue-cli 生成项目目录

    • 使用 npm -v 检测 npm 版本,尽量使用 5.X 以上版本
    • 全局安装 vue-cli, npm install vue-cli -g
    • 在终端中输入 vue init webpack 来初始化项目, 项目名需要小写
  3. 测试环境是否安装成功

    • 使用 npm run dev 进行测试环境的打开
    • 在浏览器中输入 localhost:8080 进行测试

引入 Vant 组件库

  1. 安装 Vant

    npm i vant -S

  2. 引入 Vant 的方法一 (不推荐)

    安装好 Vant 后,可以使用以前常用的方法进行引入—这是一种全局引入的方法

    1
    2
    3
    4
    5
    //	main.js

    import Vant from 'vant'
    import 'vant/lib/vant-css/index.css'
    Vue.use(vant)

    不推荐这种方法的原因是在最后打包发布的时候会增加包的大小, Vue 的 SPA 首屏打开时间本来就有些慢

  3. 引入 Vant 推荐方法

    Vant 是支持 babel-plugin-import 引入的,可以按需引入组件模块,并且不用管理我们的样式,是现在 Vue 项目组件库的主流引入方法。

    安装 babel-plugin-import

    npm i babel-plugin-import -D

    或者 npm install babel-plugin-import –save-dev

    在 .babelrc 中配置 plugins

    1
    2
    3
    4
    5
    "plugins": [
    "transform-vue-jsx",
    "transform-runtime",
    ["import",{"libraryName":"vant","style":true}]
    ]

    按需使用 Vant 组件

    在设置好 .babelrc 后,就可以按需引入 Vant 框架了。比如引入一个 Button 组件

    1
    2
    3
    4
    5
    6
    //	main.js
    import { Button } from 'vant'
    Vue.use(Button)

    // 有了这段代码之后,我们就可以在需要的组件页面中假如 Button 了
    <van-button type="primary">主要按钮</van-button>

移动端屏幕适配基础

常见移动 Web 布局适配方法

  • 固定高度,固定百分比:过时的方法
  • Media Query (媒体查询):现在比较主流的适配方案
  • flex 布局: 主流的布局方式,在项目中尽量采用 flex + rem 的方式进行布局和完成移动端的适配。

    rem 单位介绍

  • 适配原理:将 px 替换成 rem,动态修改 html 的 font-size 适配。它可以很好的根据根元素的字体大小来进行变化,从而达到各种屏幕基本一致的效果体验。

    JS 控制适配屏幕

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //	得到手机屏幕的宽度
    let htmlWidth = document.documentElement.clientWidth || document.body.clientWidth;
    // 得到 html 的 DOM 元素
    let htmlDom = document.getElementsByTagName('html')[0];
    // 设置根元素字体大小
    htmlDom.style.fontSize = htmlWidth/20 + 'px';

    // 当页面很大的时候,我们的移动适配方案会呈现很大的字体,所以加一个判断,解决页面字体过大的问题
    // 当页面宽度大于 750px 时, 把页面的宽度设置为 750px
    if ( htmlWidth&gt; 750 ) { htmlWidth=750 }

首页布局和路由设置

首页路由的配置:

打开路由配置页面:src/router/index.js,先删除 vue-cli 自动生成的 HelloWorld.Vue 的配置,然后加入新的路由配置

1
2
3
4
5
6
7
8
9
10
11
import Vue from 'vue'
import Router from 'vue-router'
import ShoppingMall from '@/components/pages/ShoppingMall' // @ 表示 项目 src 的目录

Vue.use(Router)

export default new Router({
routes: [
{path: '/',name: 'ShoppingMall',component: ShoppingMall}
]
})

建立首页组件

删除 HelloWorld.vue 文件,建立 ShoppingMall.vue 文件。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
<div>
{{msg}}
</div>
</template>

<script>
export default {
data () {
return {
msg: 'Shopping Mall'
}
}
}
</script>

Vant 布局

使用 Vant 的布局需要先进行按需引入,直接在 main.js 里引入 Row 和 Col 组件

import { Button, Row, Col } from ‘vant’

Vue.use(Button).use(Row).use(Col)

Vant 采用 24 格布局法,我们控制这 24 等分的比例就可以实现布局

1
2
3
4
5
<van-row>
<van-col span="8">span: 8</van-col>
<van-col span="8">span: 8</van-col>
<van-col span="8">span: 8</van-col>
</van-row>

首页搜索区域的布局

搜索条的布局

  1. 利用 van-row 和 van-col 快速布局一个搜索条的 HTML 骨架

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <div class="search-bar">
    <van-row>
    <van-col span="3">icon</van-col>
    <van-col span="16">serach input</van-col>
    <van-col span="5">button</van-col>
    </van-row>
    </div>

    .search-bar{
    height: 2.2rem;
    background-color: #e5017d;
    line-height:2.2rem;
    }
  2. 下载 icon 图标

    在 src/assets/ 目录下新建 images 文件夹,以后所有的项目图片都放在这里

  3. 引入图片

    把图片用 require 引进到页面中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //	通过绑定属性的方法插入图片
    <van-col span="3"><img :src="locationIcon" width="100%" /></van-col>

    export default {
    data() {
    return {
    locationIcon: require('../../assets/images/location.png')
    }
    },
    }
  4. 写 input 和 button

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    <template>
    <div>
    <!--search bar layout-->
    <div class="search-bar">
    <van-row gutter="5">
    <van-col span="3"><img :src="locationIcon" width="80%" class="location-icon" /></van-col>
    <van-col span="16">
    <input type="text" class="search-input"/>
    </van-col>
    <van-col span="5"><van-button size="mini">查找</van-button></van-col>

    </van-row>
    </div>
    </div>
    </template>

    <script>


    export default {
    data() {
    return {
    locationIcon: require('../../assets/images/location.png')
    }
    },
    }
    </script>

    <style scoped>
    .search-bar{
    height: 2.2rem;
    background-color: #e5017d;
    line-height:2.2rem;

    }
    .search-input{
    width:100%;
    height: 1.3rem;
    border-top:0px;
    border-left:0px;
    border-right:0px;
    border-bottom: 1px solid 1px !important ;
    background-color: #e5017d;
    color:#fff;
    }
    .location-icon{
    padding-top: .2rem;
    padding-left: .3rem;
    }
    </style>

首页轮播图的制作

按需加载 Swipe 组件

在 /src/main.js 下按需引入 swipe

import { Swipe, SwipeItem } from ‘vant’;

Vue.use(Swipe).use(SwipeItem);

现在的 main.js 样式如下:

import { Button, Row, Col ,Search , Swipe , SwipeItem } from ‘vant’

Vue.use(Button).use(Row).use(Col).use(Search).use(Swipe).use(SwipeItem)

下载图片

图片链接:

开始制作轮播图

在 js 部分写入一个 data 参数 bannerPicArray,把图片地址放入到里边

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//	/src/components/pages/ShoppingMall.vue

data() {
return {
locationIcon: require('../../assets/images/location.png'),
bannerPicArray:[
{imageUrl:'http://7xjyw1.com1.z0.glb.clouddn.com/simleVueDemoPic001.jpg'},
{imageUrl:'http://7xjyw1.com1.z0.glb.clouddn.com/simleVueDemoPic002.jpg'},
{imageUrl:'http://7xjyw1.com1.z0.glb.clouddn.com/simleVueDemoPic003.jpg'},
]
}
},

// 模板如下
<!--swipwer area-->
<div class="swiper-area">
<van-swipe :autoplay="1000">
<van-swipe-item v-for="(banner,index) in bannerPicArray" :key="index">
<img :src="banner.imageUrl" width="100%"/>
</van-swipe-item>
</van-swipe>
</div>

// CSS 代码
.swiper-area{
overflow: hidden;
clear: both;
width:20rem;
clear:both;
}

利用 Vant 实现图片轮播的懒加载

import { Button, Row, Col ,Search , Swipe , SwipeItem , Lazyload } from ‘vant’

Vue.use(Button).use(Row).use(Col).use(Search).use(Swipe).use(SwipeItem).use(Lazyload)

// 修改 template 区域,加入v-lazy="banner.imageUrl"就可以图片的懒加了

easyMock 和 Axios 的使用

Mock 数据准备

进入 easy-Mock

axios 的引入

直接使用 npm install 进行安装

npm install –save axios

安装之后在要使用的页面组件中进行引入

import axios from ‘axios’

然后在 created 的声明周期里取得数据

1
2
3
4
5
6
7
8
9
10
11
created(){
axios({
url: 'https://www.easy-mock.com/mock/5ae2427800247c2aa1efe442/SmileVue/',
method: 'get',
})
.then(response => {
console.log(response)
})
.catch((error) => {
})
}

如果能取得数据后,说明已经 Mock 成功了,那接下来就用这些数据进行布局

Mock 数据的使用 flex 布局

首页商品分类栏的布局

使用 flex 布局,是因为 van-row 是 24 格布局,5 个元素是不好分的,所以使用 flex 布局。

在 js 代码 created 的 axios then 方法里写入下面代码 ( 提取 Mock 数据 )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
created(){
axios({
url: 'https://www.easy-mock.com/mock/5ae2eeb23fbbf24d8cd7f0b6/SmileVue/index',
method: 'get',
})
.then(response => {
console.log(response)
if(response.status==200){
this.category=response.data.data.category;
}
})
.catch((error) => {
})
}

编写 HTML 代码

1
2
3
4
5
6
<div class="type-bar">
<div v-for="(cate,index) in category" :key="index" >
<img v-lazy="cate.image" width="90%" />
<span>{{cate.mallCategoryName}}</span>
</div>
</div>

CSS 样式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.type-bar{
background-color: #fff;
margin:0 .3rem .3rem .3rem;
border-radius: .3rem;
font-size:14px;
display: flex;
flex-direction:row;
flex-wrap:nowrap;
}
.type-bar div{
padding: .3rem;
font-size: 12px;
text-align: center;
}

广告 Banner 的布局

1
2
3
4
5
6
7
8
//	先在 created 里获取数据,然后进行 HTML 骨架编写,最后进行 CSS 样式的调整
this.adBanner = response.data.data.advertesPicture //获得广告图片

// HTML 代码编写
<!--AD banner area-->
<div class="ad-banner">
<img v-lazy="adBanner.PICTURE_ADDRESS" width="100%">
</div>

改造 swipe 组件

1
2
3
4
5
6
7
8
9
10
11
12
//	前面已经用静态数据模拟了轮播效果,现在有了Mock数据,完全可以用Mack数据代替。
先获取轮播图片地址数组。
this.bannerPicArray = response.data.data.slides //轮播图片

// 然后修改html模版中的数据绑定就可以了
<div class="swiper-area">
<van-swipe :autoplay="1000">
<van-swipe-item v-for="(banner,index) in bannerPicArray" :key="index">
<img v-lazy="banner.image" width="100%"/>
</van-swipe-item>
</van-swipe>
</div>

商品推荐 vue-awesome-swiper

先来简单的布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//	我们先把基本的布局做好,在src/components/pages/ShoppingMall.vue,里编写如下html和CSS代码,这里只是简单的布局。
<!--Recommend goods area-->
<div class="recommend-area">
<div class="recommend-title">
商品推荐
</div>
<div class="recommend-body">

</div>
</div>

// CSS
.recommend-area{
background-color: #fff;
margin-top: .3rem;
}
.recommend-title{
border-bottom:1px solid #eee;
font-size:14px;
padding:.2rem;
color:#e5017d;
}

安装 vue-awesome-swiper

npm install vue-awesome-swiper –save

引入 vue-awesome-swiper 的两种方式

  • 全局引入

    1
    2
    3
    4
    5
    6
    //	可以直接使用全局引入
    import Vue from 'vue'
    import VueAwesomeSwiper from 'vue-awesome-swiper'
    // require styles
    import 'swiper/dist/css/swiper.css'
    Vue.use(VueAwesomeSwiper, /* { default global options } */)
  • 以组件的形式引入

    1
    2
    3
    4
    5
    6
    7
    8
    9
    //	以组件形式引入	这种方式是在需要的页面以component 的形式引入,好处就是依赖性不强。
    import 'swiper/dist/css/swiper.css'
    import { swiper, swiperSlide } from 'vue-awesome-swiper'
    export default {
    components: {
    swiper,
    swiperSlide
    }
    }

获取推荐商品数据

​ 在javascript部分的data里加入recommendGoods:[]属性,然后在created生命周期里获得.

this.recommendGoods = response.data.data.recommend //推荐商品

编写 swiper 的 html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!--swiper-->
<swiper :options="swiperOption">
<swiper-slide v-for=" (item ,index) in recommendGoods" :key="index">
<div class="recommend-item">

<img :src="item.image" width="80%" />
<div>{{item.goodsName}}</div>
<div>¥{{item.price}} (¥{{item.mallPrice}})</div>
</div>
</swiper-slide>
</swiper>

// CSS
.recommend-body{
border-bottom: 1px solid #eee;
}
.recommend-item{
width:99%;
border-right: 1px solid #eee;
font-size: 12px;
text-align: center;
}

vue-awesome-swiper 详解1

vue-awesome-swiper组件在开发中是经常使用的,它可以作轮播图,可以作滚动。

一个最简单的轮播图

这里作一个单独的组件,这样不会污染项目中的文件,这个只是一个最简单默认的swiper,在components目录下新建一个文件夹swiper,然后新建一个swiperDefault.vue文件。写入如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<template>
<div >
<swiper>
<swiper-slide class="swiper-slide" v-for="(item, index) in slide" :key="index">
Slide {{item}}
</swiper-slide>
</swiper>
</div>
</template>

<script>
import 'swiper/dist/css/swiper.css'
import { swiper, swiperSlide } from 'vue-awesome-swiper'
export default {
data() {
return {
slide: [1,2,3,4,5,6]
}
},
components:{swiper,swiperSlide}
}
</script>

<style scoped>
.swiper-slide{
height: 4rem;
text-align: center;
padding-top: 3rem;
border-bottom: 1px solid #ccc;
}
</style>

代码写好后,在shoppingMall.vue文件里进行引入使用。

import swiperDefault from ‘../swiper/swiperDefault’

然后注册组件,

components:{swiper,swiperSlide,swiperDefault},

注册好后,直接在template里使用就可以了.

添加分页器

1
2
3
4
5
6
7
8
9
//	我们现在data里进行配置,代码如下:
swiperOption:{
pagination:{
el:'.swiper-pagination'
}
}

// 然后在template标签里加入一个div用于显示分页器,注意的是要在swiper-slide外层
<div class="swiper-pagination" slot="pagination"></div>

最后是在swiper标签里加入 :options="swiperOption"。就实现了有分页期的效果。

整体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<template>
<div >
<swiper :options="swiperOption">
<swiper-slide class="swiper-slide" v-for="(item, index) in slide" :key="index">
Slide {{item}}
</swiper-slide>
<div class="swiper-pagination" slot="pagination"></div>
</swiper>
</div>
</template>

<script>
import 'swiper/dist/css/swiper.css'
import { swiper, swiperSlide } from 'vue-awesome-swiper'
export default {
data() {
return {
slide: [1,2,3,4,5,6],
swiperOption:{
pagination:{
el:'.swiper-pagination'
}
}
}
},
components:{swiper,swiperSlide}
}
</script>

<style scoped>
.swiper-slide{
height: 4rem;
text-align: center;
padding-top: 3rem;
border-bottom: 1px solid #ccc;
}
</style>

竖屏切换效果

在配置项里直接配置direction就可以了,配置竖屏代码如下

1
2
3
4
5
6
swiperOption:{
direction:'vertical',
pagination:{
el:'.swiper-pagination'
}
}

整体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<template>
<div >
<swiper class="swiper" :options="swiperOption">
<swiper-slide class="swiper-slide" v-for="(item, index) in slide" :key="index">
Slide {{item}}
</swiper-slide>
<div class="swiper-pagination" slot="pagination"></div>
</swiper>
</div>
</template>

<script>
import 'swiper/dist/css/swiper.css'
import { swiper, swiperSlide } from 'vue-awesome-swiper'
export default {
data() {
return {
slide: [1,2,3,4,5,6],
swiperOption:{
direction:'vertical',
pagination:{
el:'.swiper-pagination'
}
}
}
},
components:{swiper,swiperSlide}
}
</script>

<style scoped>
.swiper-slide{
height: 4rem;
text-align: center;
line-height: 4rem;
}
.swiper{
height: 7rem;
border-top:1px solid #ccc;
border-bottom:1px solid #ccc;
}
</style>

vue-awesome-swiper 详解2

区域滚动效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<template>
<div >
<swiper class="swiper" :options="swiperOption">
<swiper-slide class="text">
<div class="centent">
一大堆文章.........
</div>
</swiper-slide>

</swiper>
</div>
</template>

<script>
import 'swiper/dist/css/swiper.css'
import { swiper, swiperSlide } from 'vue-awesome-swiper'
export default {
data() {
return {
swiperOption:{
direction:'vertical',
slidesPerView: 'auto',
freeMode:true,
mousewheel:true
}
}
},
components:{swiper,swiperSlide}
}
</script>

<style scoped>
.swiper{
height: 300px;
overflow: hidden;
}
.text{
font-size: 18px !important;
text-align: left;
padding:30px;
height: auto;
-webkit-box-sizing: border-box;
box-sizing: border-box;

}
</style>

重点看一下 options 的加入属性:

  • direction: ‘vertical’ 设置竖排显示
  • slidesPerView: ‘auto’ 设置同屏显示的数量, 默认为 1, 这里使用 auto 是随意的意思。
  • freeMode: true 默认为 false, 普通模式: slide 滑动时只滑动一格,并自动贴合 wrapper,设置为 true 则变为 free 模式, slide 会根据惯性滑动可能不止一格且不会贴合。
  • mousewheel: true 开启鼠标滚轮控制 Swiper 切换。 可是只鼠标选项, 或 true 使用默认值。

让分页器可以自由选择

在实际工作当中分页器都是可以自由选择的,只要配置一下 Options 的 clickable 数据就可以了。注意这个属性要配置在 pagination 下面,才能起作用。

1
2
3
4
pagination: {
el: '.swiper-pagination',
clickable: true
}

无线循环滚动

还有一个需求是无限循环滚动,不要到底了就要往回滚动,这个只要在 options 里加一个 loop: true 就可以实现了。

1
2
3
4
5
6
7
8
9
10
11
12
data() {
return {
slide: [1,2,3,4,5,6],
swiperOption:{
loop:true,
pagination:{
el:'.swiper-pagination',
clickable:true
}
}
}
},

首页楼层区域布局

不规则的布局

  1. 第一步: 先获取楼层一的数据

    在 data 里注册一个 floor1 的数组变量 floor1:[], 在 axios 里得到数据。

    1
    2
    3
    4
    this.floor1 = response.data.data.floor1              //楼层1数据
    this.floor1_0 =this.floor1[0]
    this.floor1_1 =this.floor1[1]
    this.floor1_2 =this.floor1[2]
  2. 第二步: 编写 HTML 代码

    在编写 HTML 的时候需要注意结构层次,原则就是先统一规划大体,然后在调整局部

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <!--floor one area-->
    <div class="floor">
    <div class="floor-anomaly">
    <div class="floor-one"><img :src="floor1_0.image" width="100%" /></div>
    <div>
    <div class="floor-two"><img :src="floor1_1.image" width="100%" /></div>
    <div><img :src="floor1_2.image" width="100%" /></div>
    </div>
    </div>
    </div>
  3. 第三步: 编写 CSS 样式

    主要使用了 flex 布局和 box-sizing

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    .floor-anomaly{
    display: flex;
    flex-direction:row;
    background-color: #fff;
    border-bottom:1px solid #ddd;
    }
    .floor-anomaly div{
    width:10rem;

    box-sizing: border-box;
    -webkit-box-sizing: border-box;
    }
    .floor-one{
    border-right:1px solid #ddd;

    }
    .floor-two{
    border-bottom:1px solid #ddd;
    }

规则部分的布局

  1. 第一步: 布局 HTML

    1
    2
    3
    4
    5
    <div class="floor-rule">
    <div v-for="(item ,index) in floor1.slice(3)" :key="index">
    <img :src="item.image" width="100%"/>
    </div>
    </div>
  2. 第二步: 编写 CSS

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    .floor-rule{
    display: flex;
    flex-direction: row;
    flex-wrap:wrap;
    background-color: #fff;

    }
    .floor-rule div{
    -webkit-box-sizing: border-box;
    box-sizing: border-box;
    width:10rem;
    border-bottom:1px solid #ddd;
    }
    .floor-rule div:nth-child(odd){
    border-right: 1px solid #ddd;
    }

楼层标题的布局

自定义样式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
/* 	完整代码	*/

<!--floor one area-->
<div class="floor">
<div class="floor-anomaly">
<div class="floor-one"><img :src="floor1_0.image" width="100%" /></div>
<div>
<div class="floor-two"><img :src="floor1_1.image" width="100%" /></div>
<div><img :src="floor1_2.image" width="100%" /></div>
</div>
</div>

<div class="floor-rule">
<div v-for="(item ,index) in floor1.slice(3)" :key="index">
<img :src="item.image" width="100%"/>
</div>
</div>
</div>


data () {
return {
locationIcon: require('../../assets/images/location.png'),
bannerPicArray:[],
category: '',
adBanner: '',
recommendGoods: [],
floor1: []
}
},

created(){
axios({
url: 'https://www.easy-mock.com/mock/5b336ab6e312d1110939a921/SmileVue/SmileVue',
method: 'get',
})
.then(response => {
// console.log(response)
if(response.status==200){
this.bannerPicArray = response.data.data.slides; //轮播图片
this.category=response.data.data.category;
this.adBanner = response.data.data.advertesPicture; //获得广告图片
this.recommendGoods = response.data.data.recommend; //推荐商品

this.floor1 = response.data.data.floor1; //楼层1数据
this.floor1_0 = this.floor1[0];
this.floor1_1 = this.floor1[1];
this.floor1_2 = this.floor1[2];
}
})


.floor-anomaly{
display: flex;
flex-direction:row;
background-color: #fff;
border-bottom:1px solid #ddd;
}
.floor-anomaly div{
width:10rem;

box-sizing: border-box;
-webkit-box-sizing: border-box;
}
.floor-one{
border-right:1px solid #ddd;

}
.floor-two{
border-bottom:1px solid #ddd;
}

.floor-rule{
display: flex;
flex-direction: row;
flex-wrap:wrap;
background-color: #fff;

}
.floor-rule div{
-webkit-box-sizing: border-box;
box-sizing: border-box;
width:10rem;
border-bottom:1px solid #ddd;
}
.floor-rule div:nth-child(odd){
border-right: 1px solid #ddd;
}

楼层组件的封装和 watch 的使用

为了代码复用和少写一些代码,将楼层这部分分封装成一个传递参数的组件,并使用 watch 来监听参数的变化,达到正确渲染的目的。

编写组件

在 src/components/ 下新建一个 component 的文件夹, 进入文件夹, 新建 floorComponent.vue 文件, 用来编写楼层组件

在编写的时候,先从页面中把相对应的 html 模板和 CSS 代码拷进来,然后再进行改造。 全部组件代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
<template>
<div>
<!--floor one area-->
<div class="floor">

<div class="floor-anomaly">
<div class="floor-one"><img :src="floorData0.image" width="100%" /></div>
<div>
<div class="floor-two"><img :src="floorData1.image" width="100%" /></div>
<div><img :src="floorData2.image" width="100%" /></div>
</div>
</div>

<div class="floor-rule">
<div v-for="(item ,index) in floorData.slice(3)" :key="index">
<img :src="item.image" width="100%"/>
</div>
</div>

</div>
</div>
</template>

<script>
export default {
props:['floorData'],
data() {
return {
floorData0:{},
floorData1:{},
floorData2:{}
}
},
created(){
//这里写得不到数据,应为数据是延迟返回的

},
watch:{
floorData:function(val){
console.log(this.floorData)
this.floorData0=this.floorData[0]
this.floorData1=this.floorData[1]
this.floorData2=this.floorData[2]
}
}

}
</script>

<style scoped>
.floor-anomaly{
display: flex;
flex-direction:row;
background-color: #fff;
border-bottom:1px solid #ddd;
}
.floor-anomaly div{
width:10rem;

box-sizing: border-box;
-webkit-box-sizing: border-box;
}
.floor-one{
border-right:1px solid #ddd;

}
.floor-two{
border-bottom:1px solid #ddd;
}

.floor-rule{
display: flex;
flex-direction: row;
flex-wrap:wrap;
background-color: #fff;

}
.floor-rule div{
-webkit-box-sizing: border-box;
box-sizing: border-box;
width:10rem;
border-bottom:1px solid #ddd;
}
.floor-rule div:nth-child(odd){
border-right: 1px solid #ddd;
}
</style>

由于组件中的数据是从远程拿来的,所以刚开始数据是空的,所以组件渲染不出来。需要加入 watch 属性来监听传递过来值的变化,当变化时, 再给 1, 2, 3副图片进行赋值操作。

最后根据赋值来改造 template 的 HTML 结构

引入组件

在 ShoppingMall.vue 中引入组件, 首先使用 import 进行引入

import floorComponent from ‘../component/floorComponent’

在 components 属性里注册组件

components: {floorComponent}

完成上面两步就可以直接在 template 里用标签的形式使用了

使用绑定属性的形式传入需要的值, 按照这种方法, 就可以直接引入其他 3 个楼层了。

删除页面中的无用代码

有了组件后,页面中的很多代码都变得无用了,我们需要作一下处理

楼层组件的完善

楼层组件标题区域制作

首先在 floorComponent.vue 组件的 template 区域加入 HTML 代码

然后编写 css 样式

1
2
3
4
5
6
.floor-title{
text-align:center;
font-size:14px;
height: 1.8rem;
line-height: 1.8rem;
}

其次加入 props 属性的编写

props: [ ‘floorData’, ‘floorTitle’ ],

这样我们就完成了楼层组件区域的制作,并且可以在说那个的时候传递过来一个标题了。

给头部区域传值

先在 ShoppingMall.vue 的 Data 里声明一个叫 floorName 的值。 然后在 axios 里进行赋值。

this.floorName = response.data.data.floorName // 楼层名称

最后修改一下 template 里的写法就可以使用了

完成其他两个楼层的代码编写

  1. 先在 data 里进行声明

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    data() {
    return {
    swiperOption: {
    slidesPerView:3,
    },
    locationIcon: require('../../assets/images/location.png'),
    bannerPicArray:[], // 轮播图片
    category:[], //商品类别标签栏
    adBanner:'', //广告图片
    recommendGoods:[], //推荐商品
    floor1:[], //楼层1的数据
    floor2:[], //楼层1的数据
    floor3:[], //楼层1的数据
    floorName:{} //楼层名称
    }
    },
  2. 在 axios 里获得数据

    1
    2
    3
    this.floor1 = response.data.data.floor1              //楼层1数据
    this.floor2 = response.data.data.floor2 //楼层2数据
    this.floor3 = response.data.data.floor3 //楼层3数据
  3. 在模板 template 里使用组件

    1
    2
    3
    <floorComponent :floorData="floor1" :floorTitle="floorName.floor1"></floorComponent>
    <floorComponent :floorData="floor2" :floorTitle="floorName.floor2"></floorComponent>
    <floorComponent :floorData="floor3" :floorTitle="floorName.floor3"></floorComponent>

Filter 在实战中的使用

编写过滤器通用方法

因为过滤器都是需要在很多组件中进行使用,所以要编写一个比较通用的方法。先在 src 文件夹下建立一个 filter 文件夹, 然后在 filter 文件夹下建立一个 moneyFilter.js 文件。

这时候就可以编写格式化钱的方法, 我们这里使用了 toFixed() 方法。

1
2
3
4
5
6
7
8
9
10
11
export function toMoney (money) {
let newMoney = money;
if (newMoney) {
newMoney = newMoney.toFixed(2);
} else {
newMoney = 0;
newMoney = newMoney.toFined(2);
}
return newMoney;
}
// 这个方法并不完善,后面会进行修改

引入 Filter

import { toMoney } from “@/filter/moneyFilter.js”

这里的 @ 代表的是 src 目录的意思,这个是 webpack 的配置,我们可以在 /build/webpack.base.conf.js 里找到这个配置项。

编写 Vue 里的 filter 属性

vue 是支持 filter 属性的,之前只是方法,现在要在 Vue 文件里编写属性.

1
2
3
4
5
6
filters: {
moneyFilter(money) {
return toMoney(money)
}
},
// 这里要注意 filter 的名字可以随便起,但是使用的 toMoney 方法,要和上边引入的一样。

在 template 中使用 filter

1
2
3
直接在使用价格的地方使用 {{ item.price | moneyFilter }} 就可以了

// 注: 上面这句话在此文章中必须要写在 代码框或者引用框 中, 否则文章无法提交到博客中, 详细报错信息稍后会在另一片文章中作出说明.

优化 filter 通用方法

1
2
3
4
//	上边的通用方法做的并不完善,在这里需要优化一下
export function toMoney(money = 0){
return money.toFixed(2)
}

首页热卖模块的 Van-list 组件的使用

html + css 部分的编写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//	html 代码,这些代码是写在 ShoppingMall.vue 文件中的
<!--Hot Area-->
<div class="hot-area">
<div class="hot-title">热卖商品</div>
<div class="hot-goods">
<!--这里需要一个list组件-->
</div>
</div>

// CSS 部分代码
.hot-area{
text-align: center;
font-size:14px;
height: 1.8rem;
line-height:1.8rem;
}

Vant 列表 (List) 组件的使用

  1. 引入 List 组件: 在 /src/main.js 文件中引入 List 组件

    import { List } from “vant”

    Vue.use( List )

  2. 构造数据: 在 data 里声明 hotGoods

    data(){ hotGoods: [] } // 热卖商品

  3. 在 axios 里获得数据

    this.hotGoods = response.data.data.hotGoods // 热卖商品

  4. 加入 List 组件,并使用 van-row 布局

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <!--Hot Area-->
    <div class="hot-area">
    <div class="hot-title">热卖商品</div>
    <div class="hot-goods">
    <van-list>
    <van-row gutter="20">
    <van-col span="12" v-for="( item, index) in hotGoods" :key="index">
    <div>{{item.name}}</div>
    </van-col>
    </van-row>
    </van-list>
    </div>
    </div>

    商品显示组件的编写

    新建 /src/component/goodsInfoComponent.vue 文件,用来制作商品组件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    <template>
    <div class="goods-info">
    <div class="goods-image">
    <img v-lazy="goodsImage" width="90%" />
    </div>
    <div class="goods-name">{{goodsName}}</div>
    <div class="goods-price">¥{{goodsPrice | moneyFilter }}</div>
    </div>
    </template>

    <script>
    import {toMoney} from '@/filter/moneyFilter.js'
    export default {
    props:['goodsImage','goodsName','goodsPrice'],
    filters:{
    moneyFilter(money){
    return toMoney(money)
    }
    },
    }
    </script>

    <style scoped>
    .goods-name{
    padding: 0 8px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space:nowrap;
    }
    </style>

    在 ShoppingMall.vue 里引入,并且在 components 里声明一下

    import goodsInfo from ‘../component/goodsInfoComponent’

    在模板中使用组件

    1
    2
    3
    4
    5
    <van-row gutter="20">
    <van-col span="12" v-for="(item,index) in hotGoods" :key="index">
    <goods-info :goodsImage="item.image" :goodsName="item.name" :goodsPrice="item.price"></goods-info>
    </van-col>
    </van-row>

编写后台服务接口配置文件

在开发中我们直接把数据接口写到了 axios 中,这样写如果地址改变或者接口改变,我们需要进入业务逻辑代码进行修改,维护起来会非常麻烦,现在我们把项目中用到的接口都单独拿出来,做一个接口配置文件 serviceAPI.config.js

编写接口配置文件

在项目 src 目录下建立 serviceAPI.config.js,代码如下:

1
2
3
4
5
6
7
const BASEURL = "https://www.easy-mock.com/mock/5ae2eeb23fbbf24d8cd7f0b6/SmileVue/"
const URL = {
getShoppingMallInfo:BASEURL+'index',
getGoodsInfo:BASEURL+'getGoodsInfo'
}

module.exports = URL

编写好后,就可以直接在要使用的文件中用 import 的形式引入。

import url from ‘@.serviceAPI.config.js’

引入后就可以直接使用了

1
2
3
4
axios({
url: url.getShoppingMallInfo,
method: 'get'
})
打赏功能
-------------本文结束感谢您的阅读-------------
0%